package com.spzx.payment.service.impl;

import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeWapPayModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeWapPayRequest;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.spzx.common.core.domain.R;
import com.spzx.common.core.exception.ServiceException;
import com.spzx.common.core.web.domain.AjaxResult;
import com.spzx.common.rabbitmq.SpzxRabbitmqService;
import com.spzx.common.rabbitmq.domain.SpzxCorrelationData;
import com.spzx.order.api.RemoteOrderService;
import com.spzx.order.api.domain.OrderInfo;
import com.spzx.payment.config.AlipayConfig;
import com.spzx.payment.domain.PaymentInfo;
import com.spzx.payment.service.AlipayService;
import com.spzx.payment.service.PaymentInfoService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Slf4j
public class AlipayServiceImpl implements AlipayService {
    @Resource
    StringRedisTemplate stringRedisTemplate;
    //当前接口：用户提交请求后希望得到支付宝的支付页，按照支付宝的要求 让浏览器访问自动提交的表单
    @Resource
    RemoteOrderService remoteOrderService;
    @Resource
    SpzxRabbitmqService spzxRabbitmqService;
    @Resource
    PaymentInfoService paymentInfoService;

    @Override
    public String submitAlipay(String orderNo) {
        //1、先加锁，再确认订单的状态：只能对待支付的订单进行支付
        //1.1 根据订单编号先查询订单的数据: 订单id、总金额、订单标题、订单状态
        R<OrderInfo> orderInfoR = remoteOrderService.getOrderInfoByOrderNo(orderNo);
        if (orderInfoR.getCode() != R.SUCCESS) {
            throw new ServiceException("订单查询异常");
        }
        OrderInfo orderInfo = orderInfoR.getData();
        if (orderInfo == null) {
            throw new ServiceException("订单查询失败");
        }
        //如果订单的状态不是待支付的状态 表示订单已经被取消、关闭 或者支付完成
        if(orderInfo.getOrderStatus()!=0){
            throw new ServiceException("订单状态不可支付");
        }
        try {
            //加锁:  加锁的key必须和 取消订单/延迟关单的key一样
            while (!stringRedisTemplate.opsForValue().setIfAbsent("spzx:order:update:lock:" + orderInfo.getId(), "1",
                    2, TimeUnit.MINUTES)) {
                //自旋获取锁
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            orderInfo = orderInfoR.getData();
            if (orderInfo == null) {
                throw new ServiceException("订单查询失败");
            }
            //如果订单的状态不是待支付的状态 表示订单已经被取消、关闭 或者支付完成
            if(orderInfo.getOrderStatus()!=0){
                throw new ServiceException("订单状态不可支付");
            }
            //执行支付后续的业务
//        System.out.println(orderInfo);


            //2、按照支付宝demo 文档要求  准备自动提交的表单
            //spzx的订单编号
            String out_trade_no = orderInfo.getOrderNo();
            //spzx的订单名称： 使用订单项名称拼接
            String subject = orderInfo.getOrderItemList().stream().map(orderItem -> orderItem.getSkuName())
                    .collect(Collectors.joining(","));
            String total_amount = orderInfo.getTotalAmount().toString();
            String body = subject;
            // 销售产品码 必填
            String product_code = "QUICK_WAP_WAY";

//            /**********************/
//            // SDK 公共请求类，包含公共请求参数，以及封装了签名与验签，开发者无需关注签名与验签
//            //调用RSA签名方式
            AlipayClient client = new DefaultAlipayClient(AlipayConfig.URL,
                    AlipayConfig.APPID, AlipayConfig.RSA_PRIVATE_KEY,
                    AlipayConfig.FORMAT, AlipayConfig.CHARSET,
                    AlipayConfig.ALIPAY_PUBLIC_KEY,
                    AlipayConfig.SIGNTYPE);
            AlipayTradeWapPayRequest alipay_request = new AlipayTradeWapPayRequest();
            // 封装请求支付信息
            AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
            model.setOutTradeNo(out_trade_no);//订单号
            model.setSubject(subject);//主题
            model.setTotalAmount(total_amount);//支付金额
            model.setBody(body);//介绍
            model.setProductCode(product_code);//产品码
            alipay_request.setBizModel(model); //设置请求体
            // 设置异步通知地址: 用户支付/取消支付 时 支付宝回调spzx接口的 地址
            alipay_request.setNotifyUrl(AlipayConfig.notify_url);
            // 设置同步地址
            alipay_request.setReturnUrl(AlipayConfig.return_url);
//            // form表单生产
            String form = "";
            try {
                // 调用SDK生成表单
                form = client.pageExecute(alipay_request).getBody();
    //            System.out.println(form);
                /*
                    保存交易日志：
                        可以将即将支付的订单信息存储到 交易日志表中，便于 异步回调时幂等性判断、支付金额  支付状态判断
                 */
                PaymentInfo paymentInfo = new PaymentInfo();
                paymentInfo.setUserId(orderInfo.getUserId());
                paymentInfo.setOrderNo(orderInfo.getOrderNo());
                paymentInfo.setPayType(2);
                paymentInfo.setContent(subject);
                paymentInfo.setAmount(orderInfo.getTotalAmount());
                paymentInfo.setPaymentStatus("0");//支付状态
                paymentInfoService.savePaymentInfoAsync(paymentInfo);
                return form;
            } catch (AlipayApiException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                throw new ServiceException("支付异常");
            }
        } finally {
            //释放锁
            stringRedisTemplate.delete("spzx:order:update:lock:" + orderInfo.getId());
        }
    }

    @Override
    public String notifyUrl(Map<String, String> params) {
        String paymentStatus = "-1";
        PaymentInfo paymentInfo = null;
        //支付宝交易号: 用户再支付宝支付时 支付宝保存支付记录的编号
        String trade_no =params.get("trade_no");
        try {
            //1、获取支付宝的通知返回参数，可参考技术文档中页面跳转同步通知参数列表(以下仅供参考)//
            //商户订单号: 提交支付请求给支付宝时传入的 spzx的订单编号
            String out_trade_no = params.get("out_trade_no");
            R<OrderInfo> orderInfoR = remoteOrderService.getOrderInfoByOrderNo(out_trade_no);
            if (orderInfoR.getCode() != R.SUCCESS) {
                throw new ServiceException("订单查询异常");
            }
            OrderInfo orderInfo = orderInfoR.getData();
            if (orderInfo == null) {
                throw new ServiceException("订单查询失败");
            }
            try {
                //加锁:  加锁的key必须和 取消订单/延迟关单的key一样
                while (!stringRedisTemplate.opsForValue().setIfAbsent("spzx:order:update:lock:" + orderInfo.getId(), "1",
                        2, TimeUnit.MINUTES)) {
                    //自旋获取锁
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
                //如果订单的状态不是待支付的状态 表示订单已经被取消、关闭 或者支付完成
                if(orderInfo.getOrderStatus()!=0){
                    log.error("支付回调，订单{} , 状态不可以支付：{}",JSON.toJSONString(orderInfo),JSON.toJSONString(params));
    //                throw new ServiceException("订单状态不可支付");
                    return "fail";
                }
                //查询交易日志：如果不存在日志
                if(StringUtils.isEmpty(out_trade_no) || (paymentInfo = paymentInfoService
                        .getOne(Wrappers.lambdaQuery(PaymentInfo.class).eq(PaymentInfo::getOrderNo,out_trade_no)))==null
                       ){
                    log.error("支付回调异常：商户订单号不存在");
                    return "fail";
                }
                //判断交易日志的状态：如果不是0 表示之前已经处理过  本次是重复提交的请求
                if(!StringUtils.equals(paymentInfo.getPaymentStatus(),"0")){
                    log.error("订单支付结果已处理");
                    return "success";
                }
                //交易状态
                String trade_status = params.get("trade_status");
                //获取支付宝的通知返回参数，可参考技术文档中页面跳转同步通知参数列表(以上仅供参考)//
                //计算得出通知验证结果
                //boolean AlipaySignature.rsaCheckV1(Map<String, String> params, String publicKey, String charset, String sign_type)
                // 2、校验接收到的参数 签名是否正确(方式数据被篡改)
                boolean verify_result = AlipaySignature.rsaCheckV1(params, AlipayConfig.ALIPAY_PUBLIC_KEY,
                        AlipayConfig.CHARSET, "RSA2");
                if(verify_result){//验证成功
                    //请在这里加上商户的业务逻辑程序代码
                    //2.1 获取订单信息
                    //校验用户支付金额是否正确
                    //获取订单金额
                    BigDecimal amount = paymentInfo.getAmount();
                    //获取用户支付金额
                    BigDecimal totalAmount = new BigDecimal(params.get("total_amount"));
                    if(amount.doubleValue() != totalAmount.doubleValue()){
                        log.error("异步回调，用户支付金额错误，应付金额：{}，实际金额：{}", amount , totalAmount);
                        paymentStatus = "-3";
                        throw new ServiceException("支付金额校验失败");
    //                    return "fail";
                    }
                    //3、验证支付宝处理的状态
                    //——请根据您的业务逻辑来编写程序（以下代码仅作参考）——
                    if(trade_status.equals("TRADE_FINISHED")){
                        //判断该笔订单是否在商户网站中已经做过处理
                        //如果没有做过处理，根据订单号（out_trade_no）在商户网站的订单系统中查到该笔订单的详细，并执行商户的业务程序
                        //请务必判断请求时的total_fee、seller_id与通知时获取的total_fee、seller_id为一致的
                        //如果有做过处理，不执行商户的业务程序

                        //注意：
                        //如果签约的是可退款协议，退款日期超过可退款期限后（如三个月可退款），支付宝系统发送该交易状态通知
                        //如果没有签约可退款协议，那么付款完成后，支付宝系统发送该交易状态通知。
                        return "success";
                    } else if (trade_status.equals("TRADE_SUCCESS")){
                        //判断该笔订单是否在商户网站中已经做过处理
                        //如果没有做过处理，根据订单号（out_trade_no）在商户网站的订单系统中查到该笔订单的详细，并执行商户的业务程序
                        //请务必判断请求时的total_fee、seller_id与通知时获取的total_fee、seller_id为一致的
                        //5、 更新交易日志的状态
                        paymentInfo.setTradeNo(trade_no);
                        paymentInfo.setPaymentStatus("1");
                        paymentInfo.setCallbackTime(new Date());
                        paymentInfo.setCallbackContent(JSON.toJSONString(params));
                        paymentInfoService.updateById(paymentInfo);
                        //6、发送订单支付成功的消息: 更新订单支付状态、更新销量库存
                        SpzxCorrelationData correlationData = new SpzxCorrelationData();
                        correlationData.setExchange("spzx.payment.exchange");
                        correlationData.setRoutingKey("spzx.payment.nofity");
                        correlationData.setRetry(true);
                        //使用Map组装发送的消息内容
                        Map<String,String> messageMap = new HashMap<>();
                        messageMap.put("type","1");//1 成功   2 失败
                        messageMap.put("orderNo" , paymentInfo.getOrderNo());
                        messageMap.put("userId",paymentInfo.getUserId().toString());
                        correlationData.setMessage(messageMap);
                        spzxRabbitmqService.sendMsg(correlationData);
                        //注意：
                        //如果签约的是可退款协议，那么付款完成后，支付宝系统发送该交易状态通知。
                        return "success";
                    }else if(trade_status.equals("TRADE_CLOSED")){
                        log.error("异步回调，订单{}未支付关闭", paymentInfo.getOrderNo());
                        paymentStatus = "-1";
                       throw new ServiceException("交易关闭");
                    }

                    //——请根据您的业务逻辑来编写程序（以上代码仅作参考）——
    //                out.clear();
    //                out.println("success");	//请不要修改或删除
                    log.error("异步回调，订单{}已被取消", paymentInfo.getOrderNo());
                    paymentStatus = "-2";
                    paymentInfo.setTradeNo(trade_no);
                    paymentInfo.setPaymentStatus("-2");
                    paymentInfo.setCallbackTime(new Date());
                    paymentInfo.setCallbackContent(JSON.toJSONString(params));
                    paymentInfoService.updateById(paymentInfo);
                    throw new ServiceException("用户取消");
    //                return "fail"; //返回成功
                    //////////////////////////////////////////////////////////////////////////////////////////
                }else{//验证失败
                    log.error("异步回调，验签失败: {} ", JSON.toJSONString(params));
                    paymentStatus = "-3";
                    throw new ServiceException("验签失败");
    //                return "fail";
                }
            } finally {
                stringRedisTemplate.delete("spzx:order:update:lock:" + orderInfo.getId());
            }
        } catch (Exception e) {
//            throw new RuntimeException(e);
//            log.error("支付回调异常：{}", ExceptionUtils.getStackTrace(e));
            //失败时 统一处理
            paymentInfo.setTradeNo(trade_no);
            paymentInfo.setPaymentStatus(paymentStatus);
            paymentInfo.setCallbackTime(new Date());
            paymentInfo.setCallbackContent(JSON.toJSONString(params));
            paymentInfoService.updateById(paymentInfo);

            SpzxCorrelationData correlationData = new SpzxCorrelationData();
            correlationData.setExchange("spzx.payment.exchange");
            correlationData.setRoutingKey("spzx.payment.nofity");
            correlationData.setRetry(true);
            //使用Map组装发送的消息内容
            Map<String,String> messageMap = new HashMap<>();
            messageMap.put("type","2");//1 成功   2 失败
            messageMap.put("orderNo" , paymentInfo.getOrderNo());
            messageMap.put("userId",paymentInfo.getUserId().toString());
            correlationData.setMessage(messageMap);
            spzxRabbitmqService.sendMsg(correlationData);
            return "fail";
        }
    }
}
